<!--
 * @Author: WuFeng <763467339@qq.com>
 * @Date: 2024-11-08 22:08:34
 * @LastEditTime: 2024-11-19 13:38:39
 * @LastEditors: WuFeng <763467339@qq.com>
 * @Description: 
 * @FilePath: \markdown-magic\src\pages\Pdf\page\entry\range.vue
 * Copyright 版权声明
-->
<template>
  <div class="pageWrap">
    <a-spin :tip="spinningConfig.tip" :spinning="spinningConfig.spinning">
      <div class="descArea">
        在线一键拆分PDF页面
      </div>
      <!-- 上传区域 -->
      <a-upload-dragger
        v-show="fileList.length === 0"
        v-model:fileList="fileList"
        accept=".pdf,.PDF"
        name="file"
        :maxCount="1"
        :multiple="false"
        :showUploadList="false"
        action=""
        :before-upload="beforeUpload"
        :customRequest="customRequest"
        list-type="picture"
        @change="handleChange"
        @drop="handleDrop"
        @remove="handleRemove"
      >
        <p class="ant-upload-drag-icon">
          <inbox-outlined></inbox-outlined>
        </p>
        <p class="ant-upload-text">点击选择或拖拽文件到这里上传</p>
        <p class="ant-upload-hint">
          支持PDF格式；最大支持100MB
        </p>
      </a-upload-dragger>
      <!-- 已上传文件内容展示区 -->
      <div 
        class="upResWrap"
        v-show="fileList.length > 0"
      >
        <h3 class="title">
          PDF页面预览列表
        </h3>
        <p class="filename">
          {{fileList.length > 0 ? fileList[0]?.name : ''}}
        </p>
        <div class="list">
          <div class="listItem" v-for="(item, index) in fileList[0]?.pagesBase64?? []" :key="index">
            <div class="info">
              <a-image
                :width="`100%`"
                :src="item"
              />
            </div>
            <div class="name">
              {{ index + 1 }}
            </div>
          </div>
        </div>
      </div>
      <!-- 配置信息区 -->
      <div class="settingWrap" v-if="fileList.length > 0">
        <a-form
          :model="fileList[0].processConfig"
          name="basic"
          :label-col="{ style: { width: '120px', paddingRight: '12px' } }"
          autocomplete="off"
        >
          <a-form-item
            label="拆分方式："
            name="way"
            :rules="[{ required: true, message: '请选择拆分方式' }]"
          >
            <a-radio-group v-model:value="fileList[0].processConfig.way">
              <a-radio :style="{
                display: 'flex',
                height: '30px',
                lineHeight: '30px',
              }" :value="1">
                <div>
                  每
                  <a-input-number v-model:value="fileList[0].processConfig.way1Value" size="small" :min="1" :max="fileList[0].pageCount" style="width: 60px;" />
                  页保存为一个PDF文件
                </div>
              </a-radio>
              <a-radio :style="{
                display: 'flex',
                height: '30px',
                lineHeight: '30px',
              }" :value="2">
                <div>
                  选择范围
                  <a-input v-model:value="fileList[0].processConfig.way2Value" placeholder="示例:1-2,3,4-7,8-10" size="small" style="width: 200px;" />
                </div>
              </a-radio>
            </a-radio-group>
          </a-form-item>
        </a-form>
      </div>
      <!-- 操作区域 -->
      <div class="btnsWrap">
        <a-space class="leftWrap">
          <a-button v-if="fileList.length > 0" danger @click="handleClear"><RedoOutlined />重新上传</a-button>
        </a-space>
        <a-space v-if="fileList.length > 0">
          <span class="selectedCount" style="margin-right: 12px;">
            文档共 {{ fileList[0].pageCount??0 }} 页
          </span>
          <a-button type="primary" @click="handleProcess"><PlayCircleOutlined />开始拆分</a-button>
          <a-button v-if="mergedPdf" type="primary" @click="handlePreview"><FilePdfOutlined />预览文件</a-button>
          <a-button v-if="mergedPdf" type="primary" ghost @click="handleDownload"><DownloadOutlined />下载</a-button>
        </a-space>
      </div>
      <!-- <pre id="output"></pre> -->
      <!-- <PdfPreview v-if="mergedPdf" :src="`http://localhost:1420/pdf1.pdf`" /> -->
      <PdfPreview v-if="mergePdfData" :src="mergePdfData" />
    </a-spin>
  </div>
</template>

<script setup>
import { ref, reactive, defineEmits, computed } from 'vue'
import { message } from 'ant-design-vue'
import {
  PlayCircleOutlined,
  FilePdfOutlined,
  RedoOutlined,
  DownloadOutlined,
  DeleteOutlined,
  FileSearchOutlined
} from '@ant-design/icons-vue'
import { PDFDocument } from 'pdf-lib'

import PdfPreview from '../../components/PdfPreview.vue'
import { downloadPdf } from '@/utils/index.js'

import dayjs from 'dayjs'

import { pdfToBase64 } from '../../service/common.js'

const emit = defineEmits([])

// loading 配置
const spinningConfig = reactive({
  spinning: false,
  tip: '处理中...'
})

const fileList = ref([])

const mergedPdf = ref(null)

const mergePdfData = ref(null)

// 预览处理后的文件
const handlePreview = async () => {
  mergePdfData.value = null
  mergePdfData.value = await mergedPdf.value.save()
}

const handleRemove = (file) => {
  return true
}

const handleChange = (info) => {
  const status = info.file.status
  if (status === 'uploading') {
  } else if (status === 'done') {
    message.success(`${info.file.name} 文件上传成功.`)
  } else if (status === 'error') {
    message.error(`${info.file.name} 文件上传错误.`)
  } else if (status === 'removed') {
    message.error(`${info.file.name} 文件删除成功.`)
  }
}
const handleDrop = (e) => {
  console.log(e)
}

const beforeUpload = (file) => {
  const isJpgOrPng = file.type === 'application/pdf';
  if (!isJpgOrPng) {
    message.error('请上传pdf格式的文件!');
  }
  const isLt2M = file.size / 1024 / 1024 < 1000;
  if (!isLt2M) {
    message.error('文件必须小于1000MB!');
  }
  file.processConfig = {
    status: '未拆分',
    // 拆分方式： 1：每x页保存为一个PDF文件 2： 拆分范围
    way: 1,
    // 每几页
    way1Value: 1,
    // 拆分范围
    way2Value: ''
  }
  return isJpgOrPng && isLt2M;
}

// 自定义上传
const customRequest = async ({ action, file, onSuccess, onError }) => {
  spinningConfig.spinning = true
  spinningConfig.tip = '上传中...'
  try {
    fileList.value.find((item, index) => {
      if (item.uid === file.uid) {
        item.status = 'done'
        
        // 读取文件
        const reader = new FileReader();
        reader.onload = async function (e) {
          // 你可以在这里处理二进制内容
          const arrayBuffer = this.result;
    
          item.arrayBuffer = arrayBuffer
    
          let existingPdf = null
          try {
            // 加载现有的 PDF 文档
            existingPdf = await PDFDocument.load(arrayBuffer);
          } catch (error) {
            message.error(`文件【${item.name}】有密码保护，无法解析！`)
            // 关闭loading
            spinningConfig.spinning = false
          }
          if (existingPdf === null) {
            fileList.value.splice(index, 1)
            return
          }
    
          // 获取所有页面
          item.existingPdf = existingPdf
          item.pages = existingPdf.getPages()
          item.pageCount = existingPdf.getPageCount()

          // 将每一页转成图片
          item.pagesBase64 = await pdfToBase64(item.arrayBuffer)
  
          // 初始化配置项
          item.processConfig = {
            status: '未拆分',
            // 拆分方式： 1：每x页保存为一个PDF文件 2： 拆分范围
            way: 1,
            // 每几页
            way1Value: 1,
            // 拆分范围
            way2Value: ''
          }
  
          // 关闭loading
          spinningConfig.spinning = false
  
        }
    
        reader.readAsArrayBuffer(item.originFileObj);
      }
      return item.uid === file.uid
    })
    console.log('-----------', fileList.value)
  } catch (error) {
    // 关闭loading
    spinningConfig.spinning = false
    message.error(error)
  }
}

// 开始拆分
const handleProcess = async () => {

  spinningConfig.spinning = true
  spinningConfig.tip = '拆分中...请耐心等待...'

  // 初始化合并后的文件
  mergePdfData.value = null
  // 创建一个新的 PDF 文档
  mergedPdf.value = await PDFDocument.create();

  const startTime = new Date()
  let totalPageCount = 0

  // 遍历文件列表
  for (const i in fileList.value) {
    const item = fileList.value[i]
    console.log(`开始处理-----------文件【${item.name}】------${dayjs().format('HH:mm:ss')}`)
    // 当前文件处理中，更新状态
    item.processConfig.status = '拆分中'
    // 读取文件
    const reader = new FileReader();
    reader.onload = async function (e) {
      
      // 获取所有页面
      const pages = item.pages
      
      // 遍历每一页
      for (const ii in pages) {

        // 当前是第一页
        const currentPage = ii * 1 + 1

        // if (currentPage >= item.processConfig.pageRange.start && currentPage <= item.processConfig.pageRange.end) {
          const page = pages[ii]
          // 将页面复制到新的 PDF 文档中
          const copiedPage = await mergedPdf.value.copyPages(item.existingPdf, [ii])
          mergedPdf.value.addPage(copiedPage[0])
          
          totalPageCount += 1
  
          console.log(`--------正在处理文件【${item.name}】的第${currentPage}页...------${dayjs().format('HH:mm:ss')}`)
        // }
  
        // 如果是最后一页处理完后，关闭loading
        if ((currentPage) === item.pageCount) {
          const endTime = new Date()
          const totalTime = (endTime - startTime) / 1000
          console.log(`---------------------------合并完成，共${totalPageCount}页，总耗时：${totalTime}秒`)
          spinningConfig.spinning = false
          message.success(`合并成功`)
          return
        }

      }
    }

    reader.readAsArrayBuffer(item.originFileObj);

  }

}

// 重新上传
const handleClear = () => {
  fileList.value = []
  mergedPdf.value = null
  mergePdfData.value = null
}

// 下载文件
const handleDownload = async () => {
  downloadPdf(await mergedPdf.value.save(), 'merged.pdf')
}
</script>

<style lang="scss" scoped>
.pageWrap {
  :deep(.ant-upload-wrapper .ant-upload-drag){
    height: auto !important;
  }
  .descArea{
    line-height: 1.6em;
    font-size: 14px;
    color: #666;
    margin-bottom: 12px;
  }
  .btnsWrap{
    margin: 16px 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .leftWrap{
      flex: 1;
    }
    .selectedCount{

    }
  }
  .upResWrap{
    background-color: #eee;
    padding: 12px;
    border-radius: 5px;
    .title{
      font-size: 16px;
      font-weight: bold;
    }
    .filename{
      font-size: 14px;
      color: #666;
      margin: 12px 0;
    }
    .list{
      display: grid;
      gap: 4px 12px;
      grid-template-columns: repeat(auto-fill,minmax(80px,1fr));
      justify-content: space-evenly;
      max-height: 450px;
      overflow-y: scroll;
      .listItem{
        text-align: center;
        .info{
          box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.2);
          border: 1px solid #ccc;
          background-color: #fff;
          border-radius: 5px;
          min-height: 80px;
          position: relative;
          overflow: auto;
          .previewBtn{
            position: absolute;
            z-index: -1;
            top: 0;
            left: 0;
            width: 40px;
            height: 28px;
            text-align: center;
            line-height: 28px;
            cursor: pointer;
            background-color: #666771;
            color: #fff;
          }
          .delBtn{
            position: absolute;
            z-index: 1;
            top: 0;
            right: 0;
            width: 60px;
            height: 28px;
            text-align: center;
            line-height: 28px;
            cursor: pointer;
            background-color: #5873fe;
            color: #fff;
          }
        }
        .name{
          padding: 10px 0;
        }

        &.active{
          .info{
            border: 4px solid #abb7f6;
            .delBtn{
              background-color: #e5404f;
              width: 70px;
            }
          }
        }
      }
    }
  }
  .settingWrap{
    background-color: #eee;
    padding: 12px;
    border-radius: 5px;
    margin-top: 12px;
  }
}
</style>